package org.lc.eic.messageconstruct.requestreply.jaxb.impl;

import java.io.InputStream;
import java.io.OutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.lc.eic.messageconstruct.jaxb.content.ContentMapper;
import org.lc.eic.messgeconstruct.requestreply.Request;
import org.lc.eic.messgeconstruct.requestreply.RequestSerializer;
import org.lc.eic.messgeconstruct.requestreply.exception.DeserializeException;
import org.lc.eic.messgeconstruct.requestreply.exception.SerializeException;
import org.lc.request._1_0.ObjectFactory;

public class JAXBRequestSerializer<I> implements RequestSerializer<I> {

	private JAXBContext context = initJAXBContext();
	private JAXBRequestMapping<I> requestMapping = new JAXBRequestMapping<I>();
	
	private GenericObjectPool marshallerPool = new GenericObjectPool(new BasePoolableObjectFactory() {	
		public Object makeObject() throws Exception {
			return context.createMarshaller();
		}	
	});
	
	private GenericObjectPool unmarshallerPool = new GenericObjectPool(new BasePoolableObjectFactory() {
		
		public Object makeObject() throws Exception {
			return context.createUnmarshaller();
		}
	
	});
	
	
	public Request<I> deserializeRequest(InputStream inputStream) throws DeserializeException {	
		try {			
			Unmarshaller unmarshaller = (Unmarshaller) unmarshallerPool.borrowObject();
			try{
				org.lc.request._1_0.Request jaxbRequest = (org.lc.request._1_0.Request) unmarshaller.unmarshal(inputStream);			
				return requestMapping.toRequest(jaxbRequest);
			}finally{
				unmarshallerPool.returnObject(unmarshaller);
			}			
		} catch (Exception e) {
			throw new DeserializeException("Failed deserialize input-stream.", e);
		} 
	}

	public void serializeRequest(Request<I> request, OutputStream outputStream) throws SerializeException {
		try {
			Marshaller marshaller = (Marshaller) marshallerPool.borrowObject();
			try{
				org.lc.request._1_0.Request jaxbRequest = requestMapping.toJAXBRequest(request);			
				marshaller.marshal(jaxbRequest, outputStream);
			} finally{
				marshallerPool.returnObject(marshaller);
			}
		} catch (Exception e) {
			throw new SerializeException("Failed serialize request.", e);
		}

	}
	
	
	
	private static JAXBContext initJAXBContext()  {
		try {
			return JAXBContext.newInstance(ObjectFactory.class.getPackage().getName(), ObjectFactory.class.getClassLoader());
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
			
	}
	
	public void setContentMapper(ContentMapper contentMapper){
		this.requestMapping.setContentMapper(contentMapper);
	}

}
